package cn.laixueit.service;

import cn.laixueit.client.BrandClient;
import cn.laixueit.client.CategoryClient;
import cn.laixueit.client.SpecClient;
import cn.laixueit.client.SpuClient;
import cn.laixueit.pojo.*;
import cn.laixueit.pojo.search.Goods;
import cn.laixueit.pojo.search.PageRequest;
import cn.laixueit.repository.GoodsRepository;
import cn.laixueit.result.PageResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchService {

    @Autowired
    private BrandClient brandClient;
    @Autowired
    private CategoryClient categoryClient;
    @Autowired
    private SpuClient spuClient;
    @Autowired
    private SpecClient specClient;

    @Autowired
    private GoodsRepository goodsRepository;

    private final ObjectMapper MAPPER = new ObjectMapper();

    public PageResult<Goods> search(PageRequest pageRequest) {
        String key = pageRequest.getKey();
        if(StringUtils.isEmpty(key)){
            return null;
        }

        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("all", key).operator(Operator.AND);
        boolQuery.must(matchQuery);

        Map<String, String> filter = pageRequest.getFilter();
        if(!CollectionUtils.isEmpty(filter)){
            for (Map.Entry<String, String> entry : filter.entrySet()) {
                String k = entry.getKey();
                if("品牌".equals(k)){
                    k = "brandId";
                }else if("分类".equals(k)){
                    k = "cid3";
                }else {
                    k = "specs."+k+".keyword";
                }
                boolQuery.filter(QueryBuilders.termQuery(k,entry.getValue()));
            }
        }

        searchQueryBuilder.withFilter(boolQuery);

        /*排序*/
        String sortBy = pageRequest.getSortBy();
        Boolean descending = pageRequest.getDescending();
        if(!StringUtils.isEmpty(sortBy)) {
            searchQueryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending ? SortOrder.DESC : SortOrder.ASC));
        }

        Integer page = pageRequest.getPage();
        Integer size = pageRequest.getSize();

        Integer elTotalPage = getTotalPage(boolQuery);
        page = page > elTotalPage ? elTotalPage : page;
        searchQueryBuilder.withPageable(org.springframework.data.domain.PageRequest.of(page-1,size));

//        Page<Goods> goodsPage = goodsRepository.search(searchQueryBuilder.build());

        //分类
        TermsAggregationBuilder cid3Aggs = AggregationBuilders.terms("cid3Agg").field("cid3");
        searchQueryBuilder.addAggregation(cid3Aggs);
        //品牌
        TermsAggregationBuilder bidAgg = AggregationBuilders.terms("bidAgg").field("brandId");
        searchQueryBuilder.addAggregation(bidAgg);

        AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) goodsRepository.search(searchQueryBuilder.build());

        long total = goodsPage.getTotalElements();
        List<Goods> goodsList = goodsPage.getContent();
        long totalPage = goodsPage.getTotalPages();

        LongTerms cid3Terms = (LongTerms) goodsPage.getAggregation("cid3Agg");
        List<Category> categoryList = getCategoryList(cid3Terms);
        LongTerms bidTerms = (LongTerms) goodsPage.getAggregation("bidAgg");
        List<Brand> brandList = getBrandList(bidTerms);

        List<Map<String,Object>> specParamsMap = null;
        if(!CollectionUtils.isEmpty(categoryList)){
            Long id = categoryList.get(0).getId();//获取第一个分类的id
            specParamsMap =  getSpecParamsAgg(id,matchQuery);
        }

        PageResult result = new PageResult<Goods>();
        result.setItems(goodsList);
        result.setTotal(total);
        result.setTotalPage(totalPage);
        result.setCategoryList(categoryList);
        result.setBrandList(brandList);
        result.setSpecList(specParamsMap);
        return result;
    }

    private List<Map<String, Object>> getSpecParamsAgg(Long id, MatchQueryBuilder matchQuery) {
        ArrayList<Map<String, Object>> specParamsMap = new ArrayList<>();
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();

        searchQueryBuilder.withFilter(matchQuery);

        List<SpecParam> specParams = specClient.specParamsByGid(null, id, true);

        specParams.forEach(specParam -> {
            searchQueryBuilder.addAggregation(AggregationBuilders.terms(specParam.getName()).field("specs."+specParam.getName()+".keyword"));
        });

        searchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));

        AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) goodsRepository.search(searchQueryBuilder.build());

        Map<String, Aggregation> aggregationMap = goodsPage.getAggregations().asMap();
        for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("k",entry.getKey());
            ArrayList<Object> options = new ArrayList<>();
            StringTerms terms = (StringTerms) entry.getValue();
            terms.getBuckets().forEach(bucket -> options.add(bucket.getKeyAsString()));
            map.put("options",options);
            specParamsMap.add(map);
        }

        return specParamsMap;
    }

    private List<Brand> getBrandList(LongTerms bidTerms) {
        List<LongTerms.Bucket> buckets = bidTerms.getBuckets();
        List<Long> bids = buckets.stream().map(bucket -> {
            long bid = bucket.getKeyAsNumber().longValue();
            return bid;
        }).collect(Collectors.toList());
        List<Brand> brandList = brandClient.findBrandList(bids);
        return brandList;
    }


    private List<Category> getCategoryList(LongTerms cid3Terms){
        List<LongTerms.Bucket> buckets = cid3Terms.getBuckets();
        List<Long> cid3List = buckets.stream().map(bucket -> {
            long cid3 = bucket.getKeyAsNumber().longValue();
            return cid3;
        }).collect(Collectors.toList());
        List<Category> categoryList = categoryClient.findCnameByList(cid3List).getBody();
        return categoryList;
    }

    /**
     * 查询cid3的分类数据
     * @return
     */
    private NativeSearchQueryBuilder getCategoryNameAggs(QueryBuilder searchQuery){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withFilter(searchQuery);
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("cidAgg").field("cid3");
        queryBuilder.addAggregation(aggregationBuilder);
        return queryBuilder;
    }

    private Integer getTotalPage(QueryBuilder queryBuilder){
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withFilter(queryBuilder);
        Page<Goods> goodsPage = goodsRepository.search(searchQueryBuilder.build());
        return goodsPage.getTotalPages();
    }

    public Goods buildGoods(Spu spu) throws IOException {
        Goods goods = new Goods();

        goods.setId(spu.getId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setSubTitle(spu.getSubTitle());
        goods.setBrandId(spu.getBrandId());
        goods.setCreateTime(spu.getCreateTime());

        String all =spu.getTitle();
        Brand brand = brandClient.getBrandById(spu.getBrandId()).getBody();
        if (brand != null) {
            all+=" "+brand.getName();
        }

        Category category = categoryClient.getById(spu.getCid3()).getBody();
        if (category != null) {
            all += " "+category.getName();
        }

        goods.setAll(all);

        List<Sku> skuList = spuClient.skuListBySpuId(spu.getId()).getBody();
        List<Long> priceList = skuList.stream().map(sku -> sku.getPrice()).collect(Collectors.toList());
        goods.setPrices(priceList);

        List<HashMap<String, Object>> skuMapList = skuList.stream().map(sku -> {
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",sku.getId());
            map.put("title",sku.getTitle());
            map.put("img", StringUtils.isEmpty(sku.getImages())?"":sku.getImages().split(",")[0]);
            map.put("price",sku.getPrice());
            return map;
        }).collect(Collectors.toList());
        try {
            goods.setSkus(MAPPER.writeValueAsString(skuMapList));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        //spu分类所有的规格参数
        List<SpecParam> specParams = specClient.specParamsByGid(null, spu.getCid3(),null);

        SpuDetail spuDetail = spuClient.spuDetail(spu.getId()).getBody();
        String genericSpec = spuDetail.getGenericSpec();
        Map<String,Object>  genericSpecMap = MAPPER.readValue(genericSpec,new TypeReference<Map<String,Object>>(){});
        String specialSpec = spuDetail.getSpecialSpec();
        Map<String,List<String>> specialSpecMap  = MAPPER.readValue(specialSpec,new TypeReference<Map<String,List<String>>>(){});

        HashMap<String, Object> paramMap = new HashMap<>();
        specParams.forEach(specParam -> {
            if (specParam.getGeneric()) {
                String value = genericSpecMap.get(specParam.getId().toString()).toString();
                if(specParam.getNumeric()){
                    value = chooseSegment(value, specParam);
                }
                paramMap.put(specParam.getName(),value);
            }else{
                paramMap.put(specParam.getName(),specialSpecMap.get(specParam.getId().toString()));
            }
        });

        goods.setSpecs(paramMap);

        return goods;
    }

    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

}
